﻿/*
libmad 3个重要API
void mad_stream_buffer(struct mad_stream *, unsigned char const *, unsigned long);
此函数把原始的未解码的 MPEG 数据和 mad_stream 数据结构关联，
以便使用 mad_frame_decode( ) 来解码 MPEG 帧数据。

int mad_frame_decode(struct mad_frame *, struct mad_stream *);
把 mad_stream 中的 MPEG 帧数据解码。

void mad_synth_frame(struct mad_synth *, struct mad_frame const *);
把解码后的音频数据合成 PCM 采样。

层次关系
               
声卡 -> pcm实例  -> playback  stream -> playback substreams
                 -> capture stream  -> capture substreams
             
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>

#include <mad.h>
#include <id3tag.h>
#include <alsa/asoundlib.h>

#include "libmad_play.h"
#include "sys.h"


/*获取mp3数据*/
extern unsigned int mp3_buf_read(unsigned char *buf, unsigned int len);

#define MEDIA_MOD_1 
/*
方式1：media停止时暂停PCM
方式2：media停止时关闭PCM
没有做足够的测试验证优劣性
*/


#define FRAME_LEN (1152)        /*mad_pcm每次携带最多1152个PCM采样数据*/
#define PCM_FRAME_LEN (1152*4)  /*1个pcm会占用4倍帧长的空间*/

#define DECODE_CACHE
#ifdef DECODE_CACHE
#define SHARED_BUFF_SIZE (PCM_FRAME_LEN*5)  /*snd_pcm_writei 是一个阻塞api，这里一次写5帧*/
static unsigned char decode_buf[SHARED_BUFF_SIZE];
#else
#define SHARED_BUFF_SIZE (PCM_FRAME_LEN) 
#endif

#if 1
#define libmad_dbg(...)  do{}while(0)
#else
#define libmad_dbg printf
#endif

static unsigned int	 shared_data_len;   
static unsigned char shared_buff[SHARED_BUFF_SIZE];
static unsigned char Mp3_InputBuffer[LIBMAD_INBUFF_SIZE];

static snd_pcm_t* handle;
static pthread_mutex_t lock;
static pthread_cond_t  empty, full;

static struct mad_stream       Stream; /*存放解码前的Bitstream 数据*/
static struct mad_frame        Frame;  /*记录MPEG 帧解码后PCM 数据的数据结构*/
static struct mad_synth        Synth;  /*存放解码合成滤波后的PCM数据*/

static volatile int decode_step = 0; 
enum{
    STEP_IDLE = 0,
    STEP_START,
    STEP_RUN,  
    STEP_STOP,  
};

static int libmad_readdata(unsigned char *buf, unsigned int len)
{
	unsigned int ret= mp3_buf_read(buf, len);
	if(ret > 0xfffff)
		return -1;
	else {
		return (int)ret;
	}
}
static unsigned int libmad_depth(void) {
    extern unsigned int mp3_buf_depth(void);
    return mp3_buf_depth();
}

static volatile unsigned char _soft_flag = 0;
static void _soft_set(void) {_soft_flag = 1;}
static unsigned char _soft_start(void) 
{
    /*
    */  
    const unsigned int threshold = 20; /*启动的值20%*/
    unsigned char ret = 0;
    unsigned int depth = libmad_depth();
    if(depth <= 1) {
        ret = 1;    /*缓冲太小了*/
    }
    else if(_soft_flag) {
        if(depth > threshold) {
            _soft_flag = 0;
        }else {
            ret = 1; /*缓冲中*/
        }
    }
    return ret;
}

static int _snd_init(unsigned int rate, int formate, int channels)
{
	
	int rc;     
	int dir=0;
    snd_pcm_hw_params_t* params=NULL;
   
    
    if(snd_pcm_open(&handle, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, 0) < 0)
	{
		perror("\nopen PCM device failed:");
		exit(1);
	}
        
    snd_pcm_hw_params_alloca(&params); //分配params结构体
	rc=snd_pcm_hw_params_any(handle, params);//初始化params
	if(rc<0)
	{
		perror("\nsnd_pcm_hw_params_any:");
		exit(1);
	}
	rc=snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);                                 //初始化访问权限
	if(rc<0)
	{
		perror("\nsed_pcm_hw_set_access:");
		exit(1);
	}

	rc=snd_pcm_hw_params_set_format(handle, params, formate);   
	if(rc<0)
	{
		perror("snd_pcm_hw_params_set_format failed:");
		exit(1);
	} 

	rc=snd_pcm_hw_params_set_channels(handle, params, channels);  
	if(rc<0)
	{
		perror("\nsnd_pcm_hw_params_set_channels:");
		exit(1);
	}    

	rc=snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);  
	if(rc<0)
	{
		perror("\nsnd_pcm_hw_params_set_rate_near:");
		exit(1);
	}   

	rc = snd_pcm_hw_params(handle, params);
	if(rc<0)
	{
		perror("\nsnd_pcm_hw_params: ");
		//exit(1);
	} 

    return 0;              
}


static inline signed int _scale(mad_fixed_t sample)
{
    sample += (1L << (MAD_F_FRACBITS - 16));
    if (sample >= MAD_F_ONE)
	sample = MAD_F_ONE - 1;
    else if (sample < -MAD_F_ONE)
	sample = -MAD_F_ONE;
    return sample >> (MAD_F_FRACBITS + 1 - 16);
}

static unsigned short _pack_pcm(struct mad_synth *Synth, unsigned char *buf, unsigned short buflen, unsigned short *sharelen) {
    unsigned char	*OutputPtr = buf;
    unsigned char 	*end = buf + buflen;

	struct mad_pcm *pcm= &(Synth->pcm);
	unsigned int nchannels, nsamples,n;
	mad_fixed_t const *left_ch, *right_ch;
	nchannels = pcm->channels;
	n  = pcm->length;
	left_ch   = pcm->samples[0];
	right_ch  = pcm->samples[1];

	for(nsamples=0; nsamples< n; nsamples++){
		signed int sample;
		sample = _scale(*left_ch++);
		*(OutputPtr++) = sample >> 0;  
		*(OutputPtr++) = sample >> 8;  
		if (nchannels == 2)  
		{  
			sample = _scale (*right_ch++);  
			*(OutputPtr++) = sample >> 0;  
			*(OutputPtr++) = sample >> 8;  
		} 
		if(OutputPtr > end){
			printf("libmad data buff use out\n");
            break;
		}
	}
    //tdbg("nsamples = %d \n", nsamples);
    *sharelen = nsamples;
    return OutputPtr - buf;
}

static unsigned char _handle_stream_err(struct mad_stream *ptream) {
    unsigned char ret = 0;
    if (MAD_RECOVERABLE(ptream->error)) {
        libmad_dbg("可恢复\n");
    } 
    else {
        if (ptream->error == MAD_ERROR_BUFLEN) {
            libmad_dbg("buffer数据不足一帧, 需要继续填充\n");
        } 
        else if (ptream->error == MAD_ERROR_LOSTSYNC) {
            libmad_dbg("丢失同步\n");
            int tagsize;
            tagsize = id3_tag_query(ptream->this_frame, ptream->bufend - ptream->this_frame);
            if (tagsize > 0) {
                mad_stream_skip(ptream, tagsize);
            }
        } 
        else {
            printf("严重错误，停止解码\n");
            libmad_media_stop();
            ret = 1;
        }
    }
    return ret;
}

#ifdef MEDIA_MOD_1
static void _pcm_pause(void)
{
    if (snd_pcm_drop(handle) < 0) {
        printf("err %s\n", __func__);
        return;
    }
}

static void _pcm_resume(void)
{
    int err;
    if (snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) {
        while ((err = snd_pcm_resume(handle)) == -EAGAIN) {
            printf("snd_pcm_state\n");
            sleep(1);
        }
    }

    if ((err = snd_pcm_prepare(handle)) < 0) {
       printf("err %s\n", __func__);
       return;
    }
}

void _pre_init(void) 
{    
    unsigned int rate = 48000;;                  //采样频率
    const int channels = 2;    
    const int format = SND_PCM_FORMAT_S16_LE;   //量化位数 16
    if (_snd_init(rate, format, channels) < 0) {
        printf("faile to init snd card\n");
        return -1;
    }
    return 0;
}
void _media_start(unsigned int rate, int channels)
{
    _pcm_resume();
}

void _media_stop(void)
{
    _pcm_pause();
}

#else 
void _pre_init(void) {}

void _media_start(unsigned int rate, int channels)
{
	const int format = SND_PCM_FORMAT_S16_LE; 	//量化位数 16
	if (_snd_init(rate, format, channels) < 0) {
		printf("faile to init snd card\n");
	}
}

void _media_stop(void)
{
    snd_pcm_close(handle);
}

#endif
static void _mad_stop(void) {
	mad_synth_finish(&Synth);
	mad_frame_finish(&Frame);
	mad_stream_finish(&Stream);
}

static void _mad_start(void) {
    _mad_stop(); /* do need ?*/
    mad_stream_init(&Stream);
	mad_frame_init(&Frame);
	mad_synth_init(&Synth);
}

void _mad_loop(void)
{
    unsigned short nsamples ;
	int err;
	/* 如果缓冲区空了或不足一帧数据, 就向缓冲区填充数据 */
	if(Stream.buffer == NULL || Stream.error == MAD_ERROR_BUFLEN) {
		size_t 			BufferSize;		/* 缓冲区大小 */
		size_t			Remaining;		/* 帧剩余数据 */
		unsigned char	*BufferStart;	/* 头指针 */

		if (Stream.next_frame != NULL) {
			/* 把剩余没解码完的数据补充到这次的缓冲区中 */
			Remaining = Stream.bufend - Stream.next_frame;
			memmove(Mp3_InputBuffer, Stream.next_frame, Remaining);
			BufferStart = Mp3_InputBuffer + Remaining;
			BufferSize = LIBMAD_INBUFF_SIZE - Remaining;
		} 
		else {
			libmad_dbg("消耗完毕\n");
			/* 设置了缓冲区地址, 但还没有填充数据 */
			BufferSize = LIBMAD_INBUFF_SIZE;
			BufferStart = Mp3_InputBuffer;
			Remaining = 0;
		}       
        if(_soft_start()) {
            usleep(100*1000);
			return; 
        }
		BufferSize = libmad_readdata(BufferStart, BufferSize);
		tdbg("BufferSize %d\n", BufferSize);
		if (BufferSize <= 0) {
			//printf("缓冲不足\n");
			usleep(200*1000);
			return;    
		}
		mad_stream_buffer(&Stream, Mp3_InputBuffer, BufferSize + Remaining);
		Stream.error = 0;
	}
	err = mad_frame_decode(&Frame, &Stream);
	if (err != 0) {
        _handle_stream_err(&Stream);
        return;
	}
	mad_synth_frame(&Synth, &Frame);

#ifdef DECODE_CACHE
    static unsigned short t_samples = 0, t_bufidx = 0;
    unsigned short buf_len;
    buf_len = _pack_pcm(&Synth, decode_buf+t_bufidx, PCM_FRAME_LEN, &nsamples);
    t_samples+=nsamples;
    t_bufidx+=buf_len;
    //myarg(t_samples);
    if(t_bufidx + PCM_FRAME_LEN < SHARED_BUFF_SIZE) {
        return;
    }
    pthread_mutex_lock(&lock);
    if (shared_data_len) {
        pthread_cond_wait(&empty, &lock);   //如果没有写完，阻塞在这里等待BUF用完
    }  

    memcpy(shared_buff, decode_buf, t_bufidx);
    shared_data_len = t_samples;
    t_samples = 0;
    t_bufidx = 0;
    pthread_cond_signal(&full); 
    pthread_mutex_unlock(&lock);
 #else
    pthread_mutex_lock(&lock);
    if (shared_data_len) {
        pthread_cond_wait(&empty, &lock);   //如果没有写完，阻塞在这里等待BUF用完
    }  
    _pack_pcm(&Synth, shared_buff, PCM_FRAME_LEN, &nsamples);
    shared_data_len = nsamples;
    pthread_cond_signal(&full); 
    pthread_mutex_unlock(&lock);
 #endif

}


void *libmad_pcm(void *parm)
{
	int err;
	while (1) 
	{
		pthread_mutex_lock(&lock);
		if (!shared_data_len) {
			pthread_cond_wait(&full, &lock);	//阻塞在这里等待full 变量
		}
		unsigned int write_len= shared_data_len;
		if ((err = snd_pcm_writei(handle, shared_buff, write_len)) < 0) {
			printf("write error: %s, errno: %d\n", snd_strerror(err), err);
			if (err == -EPIPE) {
				int errb;
				errb = snd_pcm_recover(handle, err, 0);
				if (errb < 0) {
					printf("failed to recover from underrun\n");
					return 0;
				} 
				else {
					//printf("recover\n");
				}
				snd_pcm_prepare(handle);
                _soft_set();
			}
		}
		shared_data_len = 0;
		pthread_mutex_unlock(&lock);
		pthread_cond_signal(&empty);
	}
}


void *libmad_decode(void *parm)
{
	while(1)
	{
        switch(decode_step) {
        case STEP_IDLE:
            usleep(100*1000);
            break;
        case STEP_START:
            _mad_start();
            decode_step++; 
            //break;
        case STEP_RUN:
            _mad_loop();
            break;
        case STEP_STOP:
            _mad_stop();
            decode_step = STEP_IDLE;
            break;    
        }
	}
}

void libmad_media_init(void)
{
	pthread_t thread[2];
    _pre_init();
    pthread_mutex_init(&lock, NULL);
	pthread_cond_init(&empty, NULL);
	pthread_cond_init(&full, NULL);
	pthread_create(&thread[0], NULL, libmad_decode, NULL);
	pthread_create(&thread[1], NULL, libmad_pcm, NULL);
}

void libmad_media_start(unsigned int rate, int channels)
{
    _media_start(rate, channels);
    _soft_set();
    printf("%s\n", __func__);
    decode_step = STEP_START;
    
}

void libmad_media_stop(void)
{
    printf("%s\n", __func__);
    _media_stop();
    decode_step = STEP_STOP;
}



